﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI.WebControls;
using System.Collections;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Collections.Specialized;

namespace SalaDeEnsayo.WebApplication.Pages.UserControl
{
    public class PagingGridView : GridView
    {
        #region Ctor

        public PagingGridView() : base()
        {
            this.PageIndexChanging += new GridViewPageEventHandler(KeepCheckedValues_PageIndexChanging);
            this.DataBound += new EventHandler(PagingGridView_DataBound);
            this.Sorting += new GridViewSortEventHandler(PagingGridView_Sorting); //+= new EventHandler(PagingGridView_Sorted);
            this.SetClearCheckedValues = true;
        }

        #endregion

        #region Enums
        public enum FieldPositionEnum
        {
            Left,
            Right
        }
        #endregion

        #region Constantes

        private const String KeepedCheckedValuesKey = "KeepedCheckedValues";
        private const String CustomSortDirectionKey = "SortDirKey";
        private const String CustomSortExpressionKey = "SortExpKey";
        private const String CustomRecordCountKey = "RecCntKey";
        private const String CustomPageIndexKey = "PageIdxKey";
        private const String AutoGenerateCheckKey = "AgnChkKey";
        private const String CheckPositionKey = "ChkPosKey";
        private const String ItemCheckBoxId = "ItemChkBox";
        private const String ItemCheckBoxNameFormat = "GridCheck_{0}";
        private const String HeaderCheckBoxId = "HdrChkBox";
        private const String AutoGenerateSelectKey = "AgnSltKey";
        private const String SelectPositionKey = "SltPosKey";
        private const String ItemSelectBoxId = "ItemSltBox";
        private const String ItemSelectBoxNameFormat = "GridSelect_{0}";
        private const String HeaderSelectBoxId = "HdrSltBox";
        private const String RadioButtonMarkUp = "RadioButtonMarkUp";
        #endregion

        #region Propiedades públicas

        public Int32 CustomRecordCount
        {
            get { return (ViewState[CustomRecordCountKey] as int? ?? 0); }
            set { ViewState[CustomRecordCountKey] = value; }
        }

        public Int32 CustomPageIndex
        {
            get { return (ViewState[CustomPageIndexKey] as int? ?? 0); }
            set { ViewState[CustomPageIndexKey] = value; }
        }

        public SortDirection CustomSortDirection
        {
            get { return ViewState[CustomSortDirectionKey] as SortDirection? ?? SortDirection.Ascending; }
            set { ViewState[CustomSortDirectionKey] = value; }
        }

        public string CustomSortExpression
        {
            get { return ViewState[CustomSortExpressionKey] as string ?? string.Empty; }
            set { ViewState[CustomSortExpressionKey] = value; }
        }

        public bool AutoGenerateCheck
        {
            get { return ViewState[AutoGenerateCheckKey] as bool? ?? false; }
            set { ViewState[AutoGenerateCheckKey] = value; }
        }

        public bool AutoGenerateSelect
        {
            get { return ViewState[AutoGenerateSelectKey] as bool? ?? false; }
            set { ViewState[AutoGenerateSelectKey] = value; }
        }

        public FieldPositionEnum CheckPosition
        {
            get { return ViewState[CheckPositionKey] as FieldPositionEnum? ?? FieldPositionEnum.Left; }
            set { ViewState[CheckPositionKey] = value; }
        }

        public FieldPositionEnum SelectPosition
        {
            get { return ViewState[SelectPositionKey] as FieldPositionEnum? ?? FieldPositionEnum.Left; }
            set { ViewState[SelectPositionKey] = value; }
        }

        public int[] CheckedIndexes
        {
            get
            {
                return (from row in Rows.Cast<GridViewRow>()
                        where row.FindControl(ItemCheckBoxId) as CheckBox != null &&
                            ((CheckBox)row.FindControl(ItemCheckBoxId)).Checked
                        select row.RowIndex).ToArray();
            }
        }

        public DataKey[] CheckedDataKeys
        {
            get
            {
                if ((this.DataKeyNames == null) || (this.DataKeyNames.Length == 0))
                    throw new InvalidOperationException("Deben especificarse los DataKeyNames para utilizar esta propiedad");

                if (this.KeepCheckedValues)
                {
                    if ((this.DataKeyNames == null) || (this.DataKeyNames.Length == 0))
                        throw new InvalidOperationException("Deben especificarse los DataKeyNames para utilizar esta propiedad");

                    this.KeepSelectedValues();
                    this.RemoveNotSelectedItems();
                    List<DataKey> dkeys = new List<DataKey>();
                    foreach (string keyValue in this.KeepedCheckedValues)
                    {
                        var d = new OrderedDictionary();
                        d.Add(this.DataKeyKeepCheckedValues, keyValue);
                        dkeys.Add(new DataKey(d));
                    }

                    return dkeys.ToArray();
                }
                else
                {
                    return (from index in this.CheckedIndexes
                            where DataKeys[index] != null
                            select DataKeys[index]).ToArray();
                }
            }


        }

        public bool KeepCheckedValues { get; set; }

        public string DataKeyKeepCheckedValues { get; set; }

        public object[] CheckedValues
        {
            get
            {
                if ((this.DataKeyNames == null) || (this.DataKeyNames.Length == 0))
                    throw new InvalidOperationException("Deben especificarse los DataKeyNames para utilizar esta propiedad");
                return (from index in this.CheckedIndexes
                        where DataKeys[index] != null
                        select DataKeys[index].Value).ToArray();
            }
        }

        public GridViewRow[] CheckedRows
        {
            get
            {
                return (from index in this.CheckedIndexes
                        where Rows[index] != null
                        select Rows[index]).ToArray();
            }
        }

        public int? SelectedIndex
        {
            get
            {

                if (string.IsNullOrEmpty(Page.Request.Form[this.GenerateRowSelectorName()]))
                    return null;
                else
                    return Convert.ToInt32(Page.Request.Form[this.GenerateRowSelectorName()]);


                //var selectedIndexes = (from row in Rows.Cast<GridViewRow>()
                //                       where row.FindControl(ItemSelectBoxId) as RadioButton != null &&
                //                           ((RadioButton)row.FindControl(ItemSelectBoxId)).Checked
                //                       select row.RowIndex).ToArray();
                //if (selectedIndexes.Length == 0)
                //    return null;
                //else
                //    return selectedIndexes[0];
            }
        }
        public DataKey SelectedDataKey
        {
            get
            {
                if ((this.DataKeyNames == null) || (this.DataKeyNames.Length == 0))
                    throw new InvalidOperationException("Deben especificarse los DataKeyNames para utilizar esta propiedad");
                int? selectedIndex = this.SelectedIndex;
                if (selectedIndex == null)
                    return null;
                else
                    return DataKeys[selectedIndex.Value];
            }
        }
        public object SelectedValue
        {
            get
            {
                if ((this.DataKeyNames == null) || (this.DataKeyNames.Length == 0))
                    throw new InvalidOperationException("Deben especificarse los DataKeyNames para utilizar esta propiedad");
                int? selectedIndex = this.SelectedIndex;
                if (selectedIndex == null)
                    return null;
                else
                    return DataKeys[selectedIndex.Value].Value;
            }
        }
        public GridViewRow SelectedRow
        {
            get
            {
                int? selectedIndex = this.SelectedIndex;
                if (selectedIndex == null)
                    return null;
                else
                    return Rows[selectedIndex.Value];
            }
        }
        #endregion

        #region Propiedades Privadas

        private int DataKeyCheckedIndexValue { get; set; }

        private DataKey[] CheckedDataKeysActualPage
        {
            get
            {
                if ((this.DataKeyNames == null) || (this.DataKeyNames.Length == 0))
                    throw new InvalidOperationException("Deben especificarse los DataKeyNames para utilizar esta propiedad");
                return (from index in this.CheckedIndexes
                        where DataKeys[index] != null
                        select DataKeys[index]).ToArray();
            }
        }

        private string[] KeepedCheckedValues
        {
            get { return ViewState[KeepedCheckedValuesKey] as string[] ?? null; }
            set { ViewState[KeepedCheckedValuesKey] = value; }
        }

        #endregion

        #region Overrides

        protected override void InitializePager(GridViewRow row, int columnSpan, PagedDataSource pagedDataSource)
        {
            pagedDataSource.AllowPaging = true;
            pagedDataSource.AllowCustomPaging = true;
            pagedDataSource.VirtualCount = CustomRecordCount;
            pagedDataSource.CurrentPageIndex = CustomPageIndex;
            base.InitializePager(row, columnSpan, pagedDataSource);
        }

        protected override void OnSorting(GridViewSortEventArgs e)
        {
            if (e.SortExpression == CustomSortExpression)
            {
                switch (CustomSortDirection)
                {
                    case SortDirection.Ascending:
                        e.SortDirection = SortDirection.Descending;
                        CustomSortDirection = SortDirection.Descending;
                        break;
                    case SortDirection.Descending:
                        e.SortDirection = SortDirection.Ascending;
                        CustomSortDirection = SortDirection.Ascending;
                        break;
                }
            }
            else
            {
                CustomSortExpression = e.SortExpression;
                CustomSortDirection = SortDirection.Ascending;
                e.SortDirection = SortDirection.Ascending;
            }
            base.OnSorting(e);
        }

        protected override ICollection CreateColumns(PagedDataSource dataSource, bool useDataSource)
        {
            if (AutoGenerateCheck || AutoGenerateSelect)
            {
                ICollection columns = base.CreateColumns(dataSource, useDataSource);
                var newCols = columns.Cast<DataControlField>().ToList();
                if (AutoGenerateCheck)
                {
                    var checkBoxColumn = new TemplateField()
                    {
                        HeaderTemplate = new CheckHeaderTemplate(this.ClientID),
                        ItemTemplate = new CheckItemTemplate(this.ClientID)
                    };
                    switch (this.CheckPosition)
                    {
                        case FieldPositionEnum.Left:
                            newCols.Insert(0, checkBoxColumn);
                            break;

                        case FieldPositionEnum.Right:
                            newCols.Insert(newCols.Count, checkBoxColumn);
                            break;
                    }
                }
                if (AutoGenerateSelect)
                {
                    var selectBoxColumn = new TemplateField()
                    {
                        ItemTemplate = new SelectItemTemplate(this.ClientID)
                    };
                    switch (this.SelectPosition)
                    {
                        case FieldPositionEnum.Left:
                            newCols.Insert(0, selectBoxColumn);
                            break;

                        case FieldPositionEnum.Right:
                            newCols.Insert(newCols.Count, selectBoxColumn);
                            break;
                    }
                }
                return newCols;
            }
            else
            {
                return base.CreateColumns(dataSource, useDataSource);
            }
        }

        protected override void OnRowCreated(GridViewRowEventArgs e)
        {
            base.OnRowCreated(e);
            if (e.Row.RowType == DataControlRowType.Header)
            {
                foreach (object cell in e.Row.Cells)
                {
                    var fieldCell = cell as DataControlFieldCell;
                    if (fieldCell != null)
                    {
                        var field = fieldCell.ContainingField;
                        if (field.SortExpression != "" && this.AllowSorting)
                        {
                            if (field.SortExpression == this.CustomSortExpression)
                            {
                                if (this.CustomSortDirection == SortDirection.Ascending)
                                    AddImage(fieldCell, Page.ClientScript.GetWebResourceUrl(this.GetType(), "SalaDeEnsayo.WebApplication.Images.sort_asc.gif"), "Orden ascendiente");
                                else
                                    AddImage(fieldCell, Page.ClientScript.GetWebResourceUrl(this.GetType(), "SalaDeEnsayo.WebApplication.Images.sort_desc.gif"), "Orden descendiente");
                            }
                            else
                            {
                                AddImage(fieldCell, Page.ClientScript.GetWebResourceUrl(this.GetType(), "SalaDeEnsayo.WebApplication.Images.sort_none.gif"), "Sin orden");
                            }
                        }
                    }
                }
            }
            if (e.Row.RowType == DataControlRowType.DataRow)
            {
                if (this.AutoGenerateSelect)
                {
                    // Grab a reference to the Literal control 
                    Literal output = (Literal)e.Row.FindControl(RadioButtonMarkUp);
                    // Output the markup except for the "checked" attribute 
                    output.Text = string.Format(
                        "<input type=\"radio\" name=\"" + this.GenerateRowSelectorName() + "\" "
                            + "id=\"" + this.GenerateRowSelectorName() + "{0}\" value=\"{0}\"", e.Row.RowIndex);
                    // See if we need to add the "checked" attribute 
                    if (SelectedIndex == e.Row.RowIndex) output.Text += " checked=\"checked\"";
                    // Add the closing tag 
                    output.Text += " />";
                }
            }
        }
        #endregion

        #region Métodos privados

        private void ClearCheckedValues()
        {
            this.KeepedCheckedValues = null;
        }

        private void KeepCheckedValuesOnPaging()
        {
            if (KeepCheckedValues)
            {
                KeepSelectedValues();
                RemoveNotSelectedItems();
            }
        }

        private void KeepSelectedValues()
        {
            List<string> actualDataKeysSelected = new List<string>();

            if (this.KeepedCheckedValues != null)
                actualDataKeysSelected = this.KeepedCheckedValues.ToList();

            foreach (DataKey key in this.CheckedDataKeysActualPage)
            {
                if (actualDataKeysSelected.Where(p => p == key.Values[this.DataKeyKeepCheckedValues].ToString()).Count() == 0)
                    actualDataKeysSelected.Add(key.Value.ToString());
            }

            this.KeepedCheckedValues = actualDataKeysSelected.ToArray();
        }

        private void RemoveNotSelectedItems()
        {
            //Me tengo que fijar de sacar los que se deschequearon 

            //Recorro las datakeys
            List<string> myList = this.KeepedCheckedValues.ToList();
            foreach (DataKey dkey in this.DataKeys)
            {
                //Si no esta en las this.CheckedDataKeys, es porque no tiene que guardarse como checked
                string keyValue = dkey.Values[this.DataKeyKeepCheckedValues].ToString();

                if (this.CheckedDataKeysActualPage.Where(k => k.Values[this.DataKeyKeepCheckedValues].ToString().Equals(keyValue)).Count() == 0
                    && myList.Contains(keyValue.ToString()))
                {
                    myList.RemoveAll(p => p.Equals(dkey.Value.ToString()));
                }

            }

            this.KeepedCheckedValues = myList.ToArray();
        }

        private bool SetClearCheckedValues;

        private void CheckSavedValues()
        {
            foreach (GridViewRow row in Rows.Cast<GridViewRow>())
            {
                string key = (string)this.DataKeys[row.RowIndex].Values[this.DataKeyKeepCheckedValues].ToString();

                if (this.KeepedCheckedValues != null && this.KeepedCheckedValues.Contains(key))
                    ((CheckBox)row.FindControl(ItemCheckBoxId)).Checked = true;

            }
        }

        private void AddImage(TableCell cell, string url, string alternateText)
        {
            Image sortImage = new Image()
            {
                AlternateText = alternateText,
                ImageUrl = url
            };
            if (cell.HasControls())
            {
                LinkButton button = cell.Controls[0] as LinkButton;
                if (button != null)
                {
                    LiteralControl lnkName = new LiteralControl();
                    HtmlGenericControl gv = new HtmlGenericControl("div");
                    lnkName.Text = button.Text;
                    gv.Controls.Add(lnkName);
                    gv.Controls.Add(sortImage);
                    button.Controls.Add(gv);
                }
            }
        }

        private string GenerateRowSelectorName()
        {
            return this.ClientID + "RowSelector";
        }

        private void PagingGridView_DataBound(object sender, EventArgs e)
        {
            if (this.SetClearCheckedValues)
                this.ClearCheckedValues();

            if (this.AllowPaging && this.KeepCheckedValues)
                this.CheckSavedValues();
        }

        /// <summary>
        /// Mantiene los valores seleccionador cuando se ordena
        /// </summary>
        private void PagingGridView_Sorted(object sender, EventArgs e)
        {
            if (this.AllowPaging && this.KeepCheckedValues)
            {
                this.KeepCheckedValuesOnPaging();
                this.SetClearCheckedValues = false;
            }
        }

        private void PagingGridView_Sorting(object sender, GridViewSortEventArgs e)
        {
            if (this.AllowPaging && this.KeepCheckedValues)
            {
                this.KeepCheckedValuesOnPaging();
                this.SetClearCheckedValues = false;
            }
        }


        /// <summary>
        /// Mantiene los valores checkeados cuando se cambia de pagina
        /// </summary>
        private void KeepCheckedValues_PageIndexChanging(object sender, GridViewPageEventArgs e)
        {
            if (this.AllowPaging && this.KeepCheckedValues)
            {
                this.KeepCheckedValuesOnPaging();
                this.SetClearCheckedValues = false;
            }
        }

        #endregion

        #region Clases internas

        internal class CheckItemTemplate : ITemplate
        {
            string clientGridId;
            internal CheckItemTemplate(string clientGridId)
            {
                this.clientGridId = clientGridId;
            }

            public void InstantiateIn(Control container)
            {
                CheckBox chk = new CheckBox();
                chk.ID = ItemCheckBoxId;
                chk.InputAttributes["checkref"] = string.Format(ItemCheckBoxNameFormat, clientGridId);
                container.Controls.Add(chk);
            }
        }

        internal class CheckHeaderTemplate : ITemplate
        {
            string clientGridId;
            internal CheckHeaderTemplate(string clientGridId)
            {
                this.clientGridId = clientGridId;
            }

            public void InstantiateIn(Control container)
            {
                CheckBox chk = new CheckBox();
                chk.ID = HeaderCheckBoxId;
                chk.Attributes["onclick"] = "javascript:var checked_status = this.checked; $('input[checkref=" + string.Format(ItemCheckBoxNameFormat, clientGridId) + "]').each(function() { this.checked = checked_status; });";
                container.Controls.Add(chk);
            }
        }

        internal class SelectItemTemplate : ITemplate
        {
            string clientGridId;
            internal SelectItemTemplate(string clientGridId)
            {
                this.clientGridId = clientGridId;
            }

            public void InstantiateIn(Control container)
            {
                Literal literal = new Literal();
                literal.ID = RadioButtonMarkUp;
                container.Controls.Add(literal);

                /*RadioButton select = new RadioButton();
                select.ID = ItemSelectBoxId;
                select.GroupName = clientGridId;
                container.Controls.Add(select);*/
            }
        }

        #endregion
    }
}
